/*
 * Copyright (C) 2019 ByteDance Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bytedance.scene.animation.interaction.ghostview;

import ohos.agp.components.*;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Matrix;

/**
 * <p>
 * While the platform version uses ViewOverlay, this ghost view finds the closest FrameLayout in
 * the hierarchy and adds itself there.
 * <p>
 * Since we cannot use RenderNode to delegate drawing, we instead use to
 * draw the target view. We apply the same transformation matrix applied to the target view. For
 * that, this view is sized as large as the parent FrameLayout (except padding) while the platform
 * version becomes as large as the target view.
 */
class GhostViewApi14 extends Component implements GhostViewImpl, Component.BindStateChangedListener, Component.DrawTask {

    static class Creator implements GhostViewImpl.Creator {

        @Override
        public GhostViewImpl addGhost(Component view, ComponentContainer viewGroup, Matrix matrix) {
            GhostViewApi14 ghostView = getGhostView(view);
            if (ghostView == null) {
                StackLayout frameLayout = findFrameLayout(viewGroup);
                if (frameLayout == null) {
                    return null;
                }
                ghostView = new GhostViewApi14(view);
                frameLayout.addComponent(ghostView);
            }
            ghostView.mReferences++;
            return ghostView;
        }

        @Override
        public void removeGhost(Component view) {
            GhostViewApi14 ghostView = getGhostView(view);
            if (ghostView != null) {
                ghostView.mReferences--;
                if (ghostView.mReferences <= 0) {
                    ComponentParent parent = ghostView.getComponentParent();
                    if (parent instanceof ComponentContainer) {
                        ComponentContainer group = (ComponentContainer) parent;
                        group.removeComponent(ghostView);
                    }
                }
            }
        }

        private static StackLayout findFrameLayout(ComponentContainer viewGroup) {
            while (!(viewGroup instanceof StackLayout)) {
                ComponentParent parent = viewGroup.getComponentParent();
                if (!(parent instanceof ComponentContainer)) {
                    return null;
                }
                viewGroup = (ComponentContainer) parent;
            }
            return (StackLayout) viewGroup;
        }

    }

    /**
     * The target view
     */
    final Component mView;

    /**
     * The parent of the view that is disappearing at the beginning of the animation
     */
    ComponentContainer mStartParent;

    /**
     * The view that is disappearing at the beginning of the animation
     */
    Component mStartView;

    /**
     * The number of references to this ghost view
     */
    int mReferences;

    /**
     * The horizontal distance from the ghost view to the target view
     */
    private int mDeltaX;

    /**
     * The horizontal distance from the ghost view to the target view
     */
    private int mDeltaY;

    /**
     * The current transformation matrix of the target view
     */
    Matrix mCurrentMatrix;

    /**
     * The matrix applied to the ghost view canvas
     */
    private final Matrix mMatrix = new Matrix();

    GhostViewApi14(Component view) {
        super(view.getContext());
        mView = view;
        setBindStateChangedListener(this);
        addDrawTask(this);
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
        setGhostView(mView, this);
        final int[] location = getLocationOnScreen();
        final int[] viewLocation = mView.getLocationOnScreen();
        viewLocation[0] = (int) (viewLocation[0] - mView.getTranslationX());
        viewLocation[1] = (int) (viewLocation[1] - mView.getTranslationY());
        mDeltaX = viewLocation[0] - location[0];
        mDeltaY = viewLocation[1] - location[1];
        mView.setVisibility(INVISIBLE);
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        mView.setVisibility(VISIBLE);
        setGhostView(mView, null);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        mMatrix.setMatrix(mCurrentMatrix);
        mMatrix.postTranslate(mDeltaX, mDeltaY);
        canvas.setMatrix(mMatrix);
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        mView.setVisibility(visibility == VISIBLE ? INVISIBLE : VISIBLE);
    }

    @Override
    public void reserveEndViewTransition(ComponentContainer viewGroup, Component view) {
        mStartParent = viewGroup;
        mStartView = view;
    }

    private static void setGhostView(Component view, GhostViewApi14 ghostView) {
        view.setTag(ghostView);
    }

    static GhostViewApi14 getGhostView(Component view) {
        return (GhostViewApi14) view.getTag();
    }

}